namespace Zaggit.Core
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Security.Cryptography;
    using System.Text;

    /// <summary>
    /// A singleton class providing access to encryption functionality.
    /// </summary>
    public class Crypto : ICrypto, IDisposable
    {
        #region Fields

        private readonly ICryptoTransform Decryptor;

        private readonly ICryptoTransform Encryptor;

        private static Crypto Instance = null;

        private readonly RijndaelManaged Provider;

        #endregion

        #region Constructors

        /// <summary>
        /// Prevents a default instance of the <see cref="Crypto" /> class from being created.
        /// </summary>
        private Crypto()
        {
            this.Provider = new RijndaelManaged();
            var encoding = new ASCIIEncoding();
            var key = encoding.GetBytes("Th3 Walru5 & the Carp3nter w3re waLk1ng 0n the sand".ToCharArray(), 0, 32);
            var iv = encoding.GetBytes("Twa5 br1llig anD th3 sl1thy t0ves did Gyr3 and gymbol 1n the WABe".ToCharArray(), 0, 16);
            this.Encryptor = this.Provider.CreateEncryptor(key, iv);
            this.Decryptor = this.Provider.CreateDecryptor(key, iv);
        }

        #endregion

        #region Public Methods

        /// <summary>
        /// Decrypts the specified value.
        /// </summary>
        /// <param name="value">The value.</param>
        /// <returns></returns>
        public byte[] Decrypt(byte[] value)
        {
            try
            {
                return this.Decryptor.TransformFinalBlock(value, 0, value.Length);
            }
            catch (CryptographicException)
            {
                // This data is probably from a previous version, and not encrypted.
                return value;
            }
        }

        /// <summary>
        /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
        /// </summary>
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        /// <summary>
        /// Encrypts the specified value.
        /// </summary>
        /// <param name="value">The value.</param>
        /// <returns></returns>
        public byte[] Encrypt(byte[] value)
        {
            return this.Encryptor.TransformFinalBlock(value, 0, value.Length);
        }

        /// <summary>
        /// Gets an instance of ICrypto.
        /// </summary>
        /// <returns></returns>
        public static Crypto GetInstance()
        {
            if (Instance == null) Instance = new Crypto();

            return Instance;
        }

        #endregion

        #region Protected Methods

        /// <summary>
        /// Releases unmanaged and - optionally - managed resources.
        /// </summary>
        /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                if (Provider != null)
                    Provider.Dispose();
                if (Instance != null)
                {
                    Instance.Dispose();
                    Instance = null;
                }
            }

        }

        #endregion

        #region Private Methods

        /// <summary>
        /// Finalizes an instance of the <see cref="Crypto" /> class.
        /// </summary>
        ~Crypto()
        {
            Dispose(false);
        }

        #endregion

    }
}
